package com.xt.base.acegi;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.acegisecurity.ConfigAttributeDefinition;
import org.acegisecurity.SecurityConfig;
import org.acegisecurity.intercept.web.AbstractFilterInvocationDefinitionSource;
import org.acegisecurity.intercept.web.FilterInvocationDefinition;
import org.acegisecurity.intercept.web.PathBasedFilterInvocationDefinitionMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import com.xt.base.dao.hibernate.UserDaoHibernate;
import com.xt.base.model.dictionary.Function;
import com.xt.base.model.system.Role;

/**
 * <br>
 * <br>
 * 
 * <p>
 * <a href=".java.html"><i>查看源文件</i></a>
 * </p>
 * 
 * @author 杨洪波
 * @create-date:2010-7-7
 */
public class FullPathFilterInvocationDefinitionMap extends
		AbstractFilterInvocationDefinitionSource implements
		FilterInvocationDefinition {
	// ~ Static fields/initializers
	// =====================================================================================

	private static final Log logger = LogFactory
			.getLog(PathBasedFilterInvocationDefinitionMap.class);

	// ~ Instance fields
	// ================================================================================================

	private List<EntryHolder> requestMap = new ArrayList<EntryHolder>();// = new Vector();
	private PathMatcher pathMatcher = new AntPathMatcher();
	private boolean convertUrlToLowercaseBeforeComparison = false;

	private boolean init = true;
	private UserDaoHibernate userDAO;
	private String baseAuthentication;
	//private List<String> uriLt = new ArrayList<String>();

	// ~ Methods
	// ========================================================================================================

	private String rolesStr;
	private List<SecurityConfig> acegiNameCfgLt;
	
	private boolean compareRole(List<Role> list){
		for(Role r:list){
			if(r==null) continue;
			if(rolesStr.indexOf(r.getAcegiName()+";")<0){
				return false;
			}
		}
		return true;
	}
	
	@SuppressWarnings("unchecked")
	private void initSource() {
		List<Role> list = userDAO.queryAllRoleFunctionList();
		
		if(rolesStr!=null && compareRole(list)){
			return ;
		}
		
		requestMap = new ArrayList<EntryHolder>();
		
		int size = list.size();
		Role r = null;
		Function f = null;
		Set funcSet = null;
		Map<String, Set<String>> map = new HashMap<String, Set<String>>();
		Set<String> acegiSet = null;
		Iterator iter = null;
		acegiNameCfgLt = new ArrayList<SecurityConfig>();
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < size; i++) {
			r = list.get(i);
			acegiNameCfgLt.add(new SecurityConfig(r.getAcegiName()));
			
			sb.append(r.getAcegiName()).append(";");
			
			funcSet = r.getFunctions();
			iter = funcSet.iterator();
			while (iter.hasNext()) {
				f = (Function) iter.next();
				acegiSet = map.get(f.getUrl());
				if (acegiSet == null) {
					acegiSet = new HashSet<String>();
					map.put(f.getUrl(), acegiSet);
				}
				acegiSet.add(r.getAcegiName());
			}
		}
		rolesStr  = sb.toString();
		iter = map.keySet().iterator();
		String key = null;
		ConfigAttributeDefinition att = null;
		Iterator aceIter = null;
		while (iter.hasNext()) {
			key = (String) iter.next();
			aceIter = map.get(key).iterator();
			att = new ConfigAttributeDefinition();
			while (aceIter.hasNext()) {
				att.addConfigAttribute(new SecurityConfig((String) aceIter
						.next()));
			}
			addSecureUrl(f.getUrl(), att);
		}

		parseBaseAuthentication();
		requestMap.addAll(baseAuthenticationLt);
		
		init = false;

	}
	
	private List<EntryHolder> baseAuthenticationLt=null;
	private void parseBaseAuthentication(){
		this.baseAuthentication = this.baseAuthentication==null
			?null:this.baseAuthentication.replaceAll("\t", "");
		String[] a = this.baseAuthentication==null
			?null:this.baseAuthentication.split("\n");
		
		if(a==null){
			return ;
		}
		baseAuthenticationLt=new ArrayList<EntryHolder>();
		String[] holderArr=null;
		String[] attArr=null;
		ConfigAttributeDefinition att=null;
		for(String s:a){
			if(s==null || "".equals(s.trim())){
				continue;
			}
			s = s.trim();
			holderArr = s.split("=");
			att = new ConfigAttributeDefinition();
			attArr=holderArr[1]==null?null:holderArr[1].split(",");
			for(String c:attArr) {
				att.addConfigAttribute(new SecurityConfig(c));
			}
			for(SecurityConfig sc : acegiNameCfgLt){
				att.addConfigAttribute(sc);
			}
			addSecureUrl(baseAuthenticationLt,holderArr[0],att);
		}
		init = false;
	}
	
	public void addSecureUrl(String antPath, ConfigAttributeDefinition attr){
		addSecureUrl(requestMap,antPath,attr);
	}

	public void addSecureUrl(List<EntryHolder> list,String antPath, ConfigAttributeDefinition attr) {
		if(antPath.indexOf("/")!=0){
			antPath = "/"+antPath;
		}
		list.add(new EntryHolder(antPath, attr));

		if (logger.isDebugEnabled()) {
			logger
					.debug("Added Ant path: " + antPath + "; attributes: "
							+ attr);
		}
	}

	@SuppressWarnings("unchecked")
	public Iterator getConfigAttributeDefinitions() {
		Set set = new HashSet();
		Iterator iter = requestMap.iterator();

		while (iter.hasNext()) {
			EntryHolder entryHolder = (EntryHolder) iter.next();
			set.add(entryHolder.getConfigAttributeDefinition());
		}

		return set.iterator();
	}

	public int getMapSize() {
		return this.requestMap.size();
	}

	public boolean isConvertUrlToLowercaseBeforeComparison() {
		return convertUrlToLowercaseBeforeComparison;
	}

	@SuppressWarnings("unchecked")
	public synchronized ConfigAttributeDefinition lookupAttributes(String url) {
		/*if (init) {
			parseBaseAuthentication();
		}*/
		
		//为了能实时修改角色，必须每次人/次登录都要检查角色变更情况。
		initSource();
		
		// Strip anything after a question mark symbol, as per SEC-161. See also
		// SEC-321
		int firstQuestionMarkIndex = url.indexOf("?");

		/*if (firstQuestionMarkIndex != -1) {
			url = url.substring(0, firstQuestionMarkIndex);
		}*/

		if (isConvertUrlToLowercaseBeforeComparison()) {
			url = url.toLowerCase();

			if (logger.isDebugEnabled()) {
				logger.debug("Converted URL to lowercase, from: '" + url
						+ "'; to: '" + url + "'");
			}
		}

		Iterator iter = requestMap.iterator();

		while (iter.hasNext()) {
			EntryHolder entryHolder = (EntryHolder) iter.next();

			boolean matched = pathMatcher.match(entryHolder.getAntPath(), url);

			if (logger.isDebugEnabled()) {
				logger.debug("Candidate is: '" + url + "'; pattern is "
						+ entryHolder.getAntPath() + "; matched=" + matched);
			}

			if (matched) {
				return entryHolder.getConfigAttributeDefinition();
			}
		}
		
		if (firstQuestionMarkIndex != -1) {
			url = url.substring(0, firstQuestionMarkIndex);
		}

		

		return null;
	}

	public void setConvertUrlToLowercaseBeforeComparison(
			boolean convertUrlToLowercaseBeforeComparison) {
		this.convertUrlToLowercaseBeforeComparison = convertUrlToLowercaseBeforeComparison;
	}

	// ~ Inner Classes
	// ==================================================================================================

	protected class EntryHolder {
		private ConfigAttributeDefinition configAttributeDefinition;
		private String antPath;

		public EntryHolder(String antPath, ConfigAttributeDefinition attr) {
			this.antPath = antPath;
			this.configAttributeDefinition = attr;
		}

		protected EntryHolder() {
			throw new IllegalArgumentException("Cannot use default constructor");
		}

		public String getAntPath() {
			return antPath;
		}

		public ConfigAttributeDefinition getConfigAttributeDefinition() {
			return configAttributeDefinition;
		}
	}

	public void setUserDAO(UserDaoHibernate userDAO) {
		this.userDAO = userDAO;
	}

	public String getBaseAuthentication() {
		return baseAuthentication;
	}

	public void setBaseAuthentication(String baseAuthentication) {
		this.baseAuthentication = baseAuthentication;
		
	}
}
